package com.turing.post.thirdinterface.impl;

import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;

import com.turing.post.account.util.OnlineLoadDetail;
import com.turing.post.account.util.OnlineLoadDetailDao;
import com.turing.post.cardmanagement.util.ApduExecuteObj;
import com.turing.post.cardmanagement.util.AppCardApduUtil;
import com.turing.post.cardmanagement.util.AppCardOperationBusiness;
import com.turing.post.cardmanagement.util.AppManagement;
import com.turing.post.cardmanagement.util.AppZoneInf;
import com.turing.post.cardmanagement.util.CardApdu;
import com.turing.post.cardmanagement.util.CardAppIssueFile;
import com.turing.post.cardmanagement.util.CardEPDetail;
import com.turing.post.cardmanagement.util.CardIssueRoot;
import com.turing.post.util.DESUtil;
import com.turing.post.util.PCPSApplicationException;
import com.turing.post.util.PCPSUtil;

/**
 * 燃气加气卡APP区APDU操作的具体实现
 * 
 * @author yxb
 * 
 */
@Stateless(mappedName = "thirdinterface.impl.GasAppCardOperationImpl")
@Remote
@Local
public class GasAppCardOperationImpl implements AppCardOperationBusiness {
	@EJB(mappedName = "account.impl.OnlineLoadDetailDaoImpl")
	private OnlineLoadDetailDao onlineLoadDetailDaoImpl;

//	@EJB(mappedName = "cardmanagement.impl.AppCardFaceAssocDaoImpl")
//	private AppCardFaceAssocDao appCardFaceAssocDaoImpl;
//
//	@EJB(mappedName = "common.impl.PCPSCommonDaoImpl")
//	private PCPSCommonDao pcpsCommonDaoImpl;

	/**
	 * 燃气加气卡应用的发行信息SFI
	 */
	private static final int SFI_EF16 = 0x16;

	/**
	 * 燃气加气卡应用的管理信息SFI
	 */
	private static final int SFI_EF15 = 0x15;

	/**
	 * 燃气加气卡应用的交易明细文件SFI
	 */
	private static final int SFI_EF18 = 0x18;

	/**
	 * 钱包余额上限
	 */
	private static final int EP_BALANCE_LIMIT = 100000;

	/**
	 * 产生应用发行前卡片检查APDU指令列表
	 * 
	 * @param am
	 * @param appSpecObj
	 * @return
	 * @throws PCPSApplicationException
	 */
	public ApduExecuteObj generateAppPreIssueApdu(AppManagement am)
			throws PCPSApplicationException {
		ApduExecuteObj aeo = new ApduExecuteObj();

		aeo.setAppId(am.getAppId());

		List<CardApdu> apdulist = new ArrayList<CardApdu>();

		CardApdu apdu = new CardApdu();

		// step 1 -- OPEN设备的APDU指令
		AppCardApduUtil.openDevice(apdulist, "");

		// step 2 -- 产生打开MF文件APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_SELECT_FILE);
		apdu.setApdu(AppCardApduUtil.getApduSelectMF300());
		apdulist.add(apdu);

		// step 3 -- 产生读卡片发行APDU指令(MF/0005)
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_READ_BINARY);
		apdu.setApdu(AppCardApduUtil.getApduReadBinary(0x05, true, 0, 0));
		apdulist.add(apdu);

		// step 4 -- 产生选定应用目录的APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_SELECT_FILE);
		apdu.setApdu(AppCardApduUtil.getApduSelectAppDir(am.getAID()));
		apdulist.add(apdu);

		// step 5 -- 产生读应用发行区的APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_READ_BINARY);
		apdu.setApdu(AppCardApduUtil.getApduReadBinary(SFI_EF16, true, 0, 0));
		apdulist.add(apdu);

		// step 6 -- 产生读应用管理区的APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_READ_BINARY);
		apdu.setApdu(AppCardApduUtil.getApduReadBinary(SFI_EF15, true, 0, 0));
		apdulist.add(apdu);

		// step 7 -- 产生4字节随机数
		apdu = new CardApdu();
		apdu.setApduCategory(CardApdu.APDU_ERR_STOP
				| CardApdu.APDU_GET_CHALLENGE_MAC);
		apdu.setApdu(AppCardApduUtil.getApduGetChallenge(true));
		apdulist.add(apdu);

		// 设置执行序列
		aeo.setApduList(apdulist);

		return aeo;
	}

	/**
	 * 解析应用发行前检查的APDU指令执行情况
	 * 
	 * @param am
	 * @param aeo
	 * @param cardSeqNo
	 * @return
	 * @throws PCPSApplicationException
	 */
	public AppZoneInf parseAppPreIssueApdu(AppManagement am,
			ApduExecuteObj aeo, String cardSeqNo)
			throws PCPSApplicationException {
		// 检查指令执行结果的合法性
		List<CardApdu> apdulist = aeo.getApduList();
		if (apdulist == null || apdulist.size() != 7)
			throw new PCPSApplicationException("", "无效的APDU执行结果列表!");

		int idx = 0;

		AppZoneInf azi = new AppZoneInf();

		// 检查打开情况
		String result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_OPEN, "寻卡失败!");
		result = result.substring(0, result.length() - 4);
		azi.setFullATS(result);
		azi.setATS(AppCardApduUtil.parseATS(result));

		// 检查MF目录打开情况
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_SELECT_FILE, "打开MF目录失败!");

		// 检查卡片发行区信息
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_READ_BINARY, "读卡片发行区信息失败!");
		azi.setCardIssue(CardIssueRoot.parseCardIssueRoot(result.substring(0,
				result.length() - 4)));

		// 检查应用目录信息
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_SELECT_FILE, "打开应用目录失败!");
		if (CardApdu.APDU_RESPCODE_FILE_NOTFOUND.equalsIgnoreCase(result))
			throw new PCPSApplicationException("", "卡片上不存在指定的应用目录!");

		// 检查读应用发行区信息
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_READ_BINARY, "读应用发行区失败!");
		azi.setAppIssue(CardAppIssueFile.parseCardAppIssueFile(result
				.substring(0, result.length() - 4)));

		// 检查读应用管理区信息
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_READ_BINARY, "读应用管理区失败!");
		azi.setAppSpecObj(GasCardManagementInf.parseGasCardManagementInf(result
				.substring(0, result.length() - 4)));

		// 检查4字节随机数
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_GET_CHALLENGE_MAC, "取MAC随机数失败!");
		if (result.length() != 12)
			throw new PCPSApplicationException("", "MAC随机数长度非法!");
		azi.setMacRandom(result.substring(0, 8));

		// 检查卡片发行区信息
		CardIssueRoot cir = azi.getCardIssue();

		// 检查发卡商的一致性
		if (PCPSUtil.compareObject(cir.getIssueCompanyNo(), am
				.getIssueCompanyNo()))
			throw new PCPSApplicationException("", "卡片非本发卡商发行的卡片!");

		// 检查卡序号的一致性
		if (PCPSUtil.compareObject(cir.getCardSeqNo(), cardSeqNo))
			throw new PCPSApplicationException("", "卡内预置的卡序号与本次发行期望的卡序号不一致!");

		// 检查应用发行区信息
		CardAppIssueFile caif = azi.getAppIssue();

		// 检查发卡商的一致性
		if (PCPSUtil.compareObject(caif.getIssueCompanyNo(), am
				.getIssueCompanyNo()))
			throw new PCPSApplicationException("", "卡片非本发卡商发行的卡片!");

		// 检查卡序号的一致性
		if (PCPSUtil.compareObject(caif.getCardSeqNo(), cardSeqNo))
			throw new PCPSApplicationException("", "发行区的卡序号与本次发行期望的卡序号不一致!");

		// 检查管理区
		GasCardManagementInf gcmi = (GasCardManagementInf) azi.getAppSpecObj();
		if (!gcmi.maybeIssue())
			throw new PCPSApplicationException("", "管理区非可发行状态!");

		return azi;
	}

	/**
	 * 产生应用发行真正APDU指令列表
	 * 
	 * @param am
	 * @param azi
	 * @return
	 * @throws PCPSApplicationException
	 */
	public ApduExecuteObj generateAppIssueApdu(AppManagement am, AppZoneInf azi)
			throws PCPSApplicationException {

		ApduExecuteObj aeo = new ApduExecuteObj();

		aeo.setAppId(am.getAppId());

		List<CardApdu> apdulist = new ArrayList<CardApdu>();

		CardApdu apdu = new CardApdu();

		// 管理信息
		GasCardManagementInf gcmi = (GasCardManagementInf) azi.getAppSpecObj();

		// 状态置为正常
		gcmi.setStatus(GasCardManagementInf.STATUS_NORMAL);

		// 此处计算安全报文MAC应该按照DAMK及应用序列号进行分散得到真正的应用维护子密钥

		// 执行文件内容编码
		String befmac = AppCardApduUtil.getApduWriteBinary(true, SFI_EF15,
				true, 0, PCPSUtil.getBytes(gcmi.encodeGasCardManagementInf()));
		String sapdu = befmac
				+ DESUtil.genMsgMac(CardApdu.DEFAULT_KEY_16,
						azi.getMacRandom(), befmac);

		// step 1 -- 以线路保护方式写文件
		apdu = new CardApdu();
		apdu.setApduCategory(CardApdu.APDU_ERR_STOP
				| CardApdu.APDU_WRITE_BINARY);
		apdu.setApdu(sapdu);
		apdulist.add(apdu);

		// step 2 -- CLOSE
		AppCardApduUtil.closeDevice(apdulist);

		// 设置执行序列
		aeo.setApduList(apdulist);

		return aeo;
	}

	/**
	 * 解析应用发行APDU结果
	 * 
	 * @param am
	 * @param aeo
	 * @throws PCPSApplicationException
	 */
	public void parseAppIssueApduResult(AppManagement am, ApduExecuteObj aeo)
			throws PCPSApplicationException {
		// 检查指令执行结果的合法性
		List<CardApdu> apdulist = aeo.getApduList();
		if (apdulist == null || apdulist.size() != 2)
			throw new PCPSApplicationException("", "无效的APDU执行结果列表!");

		int idx = 0;

		// 检查更新情况
		AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_WRITE_BINARY, "更新卡片应用目录管理信息失败!");
	}

	/**
	 * 产生读应用区域的APDU指令列表
	 */
	public ApduExecuteObj generateReadAppZoneApdu(AppManagement am)
			throws PCPSApplicationException {
		ApduExecuteObj aeo = new ApduExecuteObj();

		aeo.setAppId(am.getAppId());

		List<CardApdu> apdulist = new ArrayList<CardApdu>();

		CardApdu apdu = new CardApdu();

		// step 1 -- OPEN设备的APDU指令
		AppCardApduUtil.openDevice(apdulist, "");

		// step 2 -- 产生选定应用目录的APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_SELECT_FILE);
		apdu.setApdu(AppCardApduUtil.getApduSelectAppDir(am.getAID()));
		apdulist.add(apdu);

		// step 3 -- 产生读应用基本信息区的APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_READ_BINARY);
		apdu.setApdu(AppCardApduUtil.getApduReadBinary(SFI_EF16, true, 0, 0));
		apdulist.add(apdu);

		// step 4 -- 产生读应用管理区的APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_READ_BINARY);
		apdu.setApdu(AppCardApduUtil.getApduReadBinary(SFI_EF15, true, 0, 0));
		apdulist.add(apdu);

		// step 5 -- 产生读电子钱包余额的APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_GET_BALANCE);
		apdu.setApdu(AppCardApduUtil.getApduGetEPBalance());
		apdulist.add(apdu);

		// step 6 -- 产生读交易明细记录的APDU指令
		for (int i = 0; i < 10; i++) {
			apdu = new CardApdu();
			apdu.setApduCategory(CardApdu.APDU_ERR_IGNORE
					| CardApdu.APDU_GET_EPLIST);
			apdu.setApdu(AppCardApduUtil.getApduReadRecord(SFI_EF18, i + 1));
			apdulist.add(apdu);
		}

		// step 7 -- CLOSE设备的APDU指令
		AppCardApduUtil.closeDevice(apdulist);

		// 设置执行序列
		aeo.setApduList(apdulist);

		return aeo;
	}

	/**
	 * 将APDU指令的结果进行解析,返回本应用特定的对象
	 * 
	 * @param am
	 * @return
	 */
	public AppZoneInf parseAppZoneObject(AppManagement am, ApduExecuteObj aeo)
			throws PCPSApplicationException {
		// 检查指令执行结果的合法性
		List<CardApdu> apdulist = aeo.getApduList();
		if (apdulist == null || apdulist.size() != 16)
			throw new PCPSApplicationException("", "无效的APDU执行结果列表!");

		int idx = 0;

		AppZoneInf azi = new AppZoneInf();

		// 检查打开情况
		String result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_OPEN, "寻卡失败!");
		result = result.substring(0, result.length() - 4);
		azi.setFullATS(result);
		azi.setATS(AppCardApduUtil.parseATS(result));

		// 检查应用目录打开情况
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_SELECT_FILE, "打开应用目录失败!");

		// 检查应用发行区信息
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_READ_BINARY, "读应用发行区信息失败!");
		azi.setAppIssue(CardAppIssueFile.parseCardAppIssueFile(result
				.substring(0, result.length() - 4)));

		// 检查应用管理区信息
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_READ_BINARY, "读应用发行区信息失败!");
		azi.setAppSpecObj(GasCardManagementInf.parseGasCardManagementInf(result
				.substring(0, result.length() - 4)));

		// 检查应用电子钱包余额
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_GET_BALANCE, "读应用电子钱包余额失败!");
		azi.setEpbalance(PCPSUtil.parseNetworkInteger(PCPSUtil.getBytes(result
				.substring(0, 8)), 0, 4));

		List<CardEPDetail> eplist = new ArrayList<CardEPDetail>();

		// 检查应用交易明细
		for (int i = 0; i < 10; i++) {
			result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
					CardApdu.APDU_GET_EPLIST, "读应用交易明细失败!");
			// 如果读到最后一条记录
			if (PCPSUtil.compareObject(result, CardApdu.APDU_NO_REC_FOUND))
				break;
			result = result.substring(0, 46);
			// 加入到明细列表
			eplist.add(CardEPDetail.parseEPDetail(PCPSUtil.getBytes(result)));
		}

		azi.setEplist(eplist);

		return azi;
	}

	/**
	 * 将指定应用特定的对象进行HTML渲染,以驱动客户端展现
	 * 
	 * @param am
	 * @param appObj
	 * @return
	 * @throws PCPSApplicationException
	 */
	public String renderAppZoneObject(AppManagement am, AppZoneInf appObj)
			throws PCPSApplicationException {
		// 需要具体的实现
		GasCardManagementInf gcmInfo = (GasCardManagementInf) appObj
				.getAppSpecObj();
		StringBuilder sb = new StringBuilder();
		sb.append("<table cellSpacing=0 cellPadding=2 class='tableForm'>");
		sb.append("<tr><td width='30%' class='labelTd'>版本号</td>");
		sb.append("<td class='formTd'>"+gcmInfo.VERSION_01+"</td></tr>");
		sb.append("<tr><td class='labelTd'>气瓶编号</td>");
		sb.append("<td class='formTd'>"+PCPSUtil.encodeAscToHexString(gcmInfo.getGasBottleNo(), 30)+"</td></tr>");
		sb.append("<tr><td class='labelTd'>车架号</td>");
		sb.append("<td class='formTd'>"+PCPSUtil.encodeAscToHexString(gcmInfo.getVin(), 30)+"</td></tr>");
		sb.append("<tr><td class='labelTd'>注册编号</td>");
		sb.append("<td class='formTd'>"+PCPSUtil.encodeAscToHexString(gcmInfo.getRegNo(), 10)+"</td></tr>");
		sb.append("<tr><td class='labelTd'>车种类</td>");
		sb.append("<td class='formTd'>"+PCPSUtil.encodeAscToHexString(gcmInfo.getVehicleType(), 2)+"</td></tr>");
		sb.append("<tr><td class='labelTd'>车牌号</td>");
		sb.append("<td class='formTd'>"+PCPSUtil.encodeAscToHexString(gcmInfo.getVehicleNo(), 12)+"</td></tr>");
		sb.append("<tr><td class='labelTd'>到检日期</td>");
		sb.append("<td class='formTd'>"+gcmInfo.getExpired()+"</td></tr>");
		sb.append("<tr><td class='labelTd'>气瓶数量</td>");
		sb.append("<td class='formTd'>"+PCPSUtil.getByteHexString(gcmInfo.getGasBottleCount())+"</td></tr>");
		sb.append("<tr><td class='labelTd'>状态标志</td>");
		sb.append("<td class='formTd'>"+gcmInfo.getStatus()+"</td></tr>");
		sb.append("<tr><td class='labelTd'>车主名称</td>");
		sb.append("<td class='formTd'>"+PCPSUtil.encodeAscToHexString(gcmInfo.getOwner(), 60)+"</td></tr>");
		sb.append("</table>");
		return sb.toString();
	}

	/**
	 * 产生圈存初始化APDU指令列表
	 * 
	 * @param am
	 * @param appObj
	 *            圈存前读出的卡内应用对象
	 * @param loadSeqNo
	 * @return
	 */
	public ApduExecuteObj generateLoadInitApdu(AppManagement am,
			AppZoneInf appObj, String loadSeqNo)
			throws PCPSApplicationException {

		if (appObj == null || appObj.getAppSpecObj() == null)
			throw new PCPSApplicationException("", "无效的卡对象!");

		Object obj = appObj.getAppSpecObj();
		if (!(obj instanceof GasCardManagementInf))
			throw new PCPSApplicationException("", "无效的燃气加气应用对象!");

		// 卡片对象
		GasCardManagementInf gcmi = (GasCardManagementInf) obj;

		// 圈存流水
		OnlineLoadDetail old = onlineLoadDetailDaoImpl
				.getOnlineLoadDetailByLocalSeqNo(loadSeqNo);
		if (old == null)
			throw new PCPSApplicationException("", "无效的圈存流水" + loadSeqNo + "!");

		// 检查交易类型
		if (!PCPSUtil.compareObject(old.getTranType(),
				OnlineLoadDetail.OnlineLoad_TranType_Deposit))
			throw new PCPSApplicationException("", "流水" + loadSeqNo
					+ "非圈存交易流水!");

		// 检查交易状态
		if (!PCPSUtil.compareObject(old.getStatus(),
				OnlineLoadDetail.OnlineLoad_Status_Init))
			throw new PCPSApplicationException("", "流水" + loadSeqNo
					+ "非圈存初始化状态!");

		// 检查终端的合法性
		if (AppCardApduUtil.isValidateTerminal(old.getTerminalNo()))
			throw new PCPSApplicationException("", "流水[" + loadSeqNo + "]圈存终端["
					+ old.getTerminalNo() + "]非法!");

		ApduExecuteObj aeo = new ApduExecuteObj();

		aeo.setAppId(am.getAppId());

		List<CardApdu> apdulist = new ArrayList<CardApdu>();

		CardApdu apdu = new CardApdu();

		// step 1 -- OPEN设备的APDU指令
		AppCardApduUtil.openDevice(apdulist, appObj.getFullATS());

		// step 2 -- 产生选定应用目录的APDU指令
		apdu = new CardApdu();
		apdu
				.setApduCategory(CardApdu.APDU_ERR_STOP
						| CardApdu.APDU_SELECT_FILE);
		apdu.setApdu(AppCardApduUtil.getApduSelectAppDir(am.getAID()));
		apdulist.add(apdu);

		// step 3 -- 产生圈存初始化的APDU指令
		apdu = new CardApdu();
		apdu.setApduCategory(CardApdu.APDU_ERR_STOP | CardApdu.APDU_LOAD_INIT);
		apdu.setApdu(AppCardApduUtil.getApduLoadInit(0, old.getAmount()
				.intValue(), old.getTerminalNo()));
		apdulist.add(apdu);

		// 设置执行序列
		aeo.setApduList(apdulist);

		// 更新圈存流水中的KeyIdx为本次选定的索引号
		old.setKeyIdx("00");
		onlineLoadDetailDaoImpl.update(old);

		// 设置圈存流水号
		aeo.setLoadSeqNo(loadSeqNo);

		return aeo;
	}

	/**
	 * 产生圈存APDU指令
	 * 
	 * @param old
	 * @return
	 * @throws PCPSApplicationException
	 */
	private ApduExecuteObj generateLoadApdu(OnlineLoadDetail old)
			throws PCPSApplicationException {

		ApduExecuteObj aeo = new ApduExecuteObj();

		aeo.setAppId(old.getAppId());

		List<CardApdu> apdulist = new ArrayList<CardApdu>();

		CardApdu apdu = new CardApdu();

		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");

		String stranDate = sdf.format(old.getTranTime());

		// step 1 -- 产生圈存APDU指令
		apdu = new CardApdu();
		apdu.setApduCategory(CardApdu.APDU_ERR_STOP | CardApdu.APDU_LOAD);
		apdu.setApdu(AppCardApduUtil.getApduLoad(stranDate.substring(0, 8),
				stranDate.substring(8, 14), old.getMac2()));
		apdulist.add(apdu);

		// 设置执行序列
		aeo.setApduList(apdulist);

		// 设置圈存流水号
		aeo.setLoadSeqNo(old.getLoadSeqNo());

		return aeo;
	}

	/**
	 * 产生圈存过程密钥
	 * 
	 * @param pseudonum
	 * @param seqno
	 * @return
	 */
	private String generateLoadSK(String dlk, String pseudonum, String seqno) {
		// 产生过程密钥
		String sk = DESUtil.genSK(dlk, pseudonum + seqno + "8000");
		return sk;
	}

	/**
	 * 校验圈存初始化的MAC1
	 * 
	 * @param loadSK
	 * @param oldbal
	 * @param amount
	 * @param terminalNo
	 * @return
	 */
	private boolean verifyLoadMac1(String loadSK, String oldbal, String amount,
			String terminalNo, String mac1) {
		// 计算摘要的数据
		String data = oldbal + amount + CardEPDetail.TRANTYPE_EP_LOAD
				+ terminalNo;
		String wait_mac1 = DESUtil.genMAC(loadSK, data);
		return wait_mac1.equalsIgnoreCase(mac1);
	}

	/**
	 * 产生圈存MAC2
	 * 
	 * @param loadSK
	 * @param amount
	 * @param terminalNo
	 * @param tranTime
	 * @return
	 */
	private String genLoadMac2(String loadSK, String amount, String terminalNo,
			Date tranTime) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");

		// 计算摘要的数据
		String data = amount + CardEPDetail.TRANTYPE_EP_LOAD + terminalNo
				+ sdf.format(tranTime);
		return DESUtil.genMAC(loadSK, data);
	}

	/**
	 * 校验圈存TAC
	 * 
	 * @param dtk
	 * @param newBal
	 * @param oldSeqNo
	 * @param amount
	 * @param terminalNo
	 * @param tranTime
	 * @param tac
	 * @return
	 */
	private boolean verifyTac(String dtk, String newBal, String oldSeqNo,
			String amount, String terminalNo, Date tranTime, String tac) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		String data = newBal + oldSeqNo + amount
				+ CardEPDetail.TRANTYPE_EP_LOAD + terminalNo
				+ sdf.format(tranTime);
		String wait_mac1 = DESUtil.genTAC(dtk, data);
		return wait_mac1.equalsIgnoreCase(tac);
	}

	/**
	 * 解析圈存初始化结果,根据其结果产生圈存APDU指令列表
	 * 
	 * @param am
	 * @param aeo
	 * @return
	 */
	public ApduExecuteObj generateLoadApdu(AppManagement am, ApduExecuteObj aeo)
			throws PCPSApplicationException {

		if (aeo == null)
			throw new PCPSApplicationException("", "无效的APDU执行/结果对象!");

		String loadSeqNo = aeo.getLoadSeqNo();

		// 圈存流水
		OnlineLoadDetail old = onlineLoadDetailDaoImpl
				.getOnlineLoadDetailByLocalSeqNo(loadSeqNo);
		if (old == null)
			throw new PCPSApplicationException("", "无效的圈存流水" + loadSeqNo + "!");

		// 检查交易类型
		if (!PCPSUtil.compareObject(old.getTranType(),
				OnlineLoadDetail.OnlineLoad_TranType_Deposit))
			throw new PCPSApplicationException("", "流水" + loadSeqNo
					+ "非圈存交易流水!");

		// 检查交易状态
		if (!PCPSUtil.compareObject(old.getStatus(),
				OnlineLoadDetail.OnlineLoad_Status_Init))
			throw new PCPSApplicationException("", "流水" + loadSeqNo
					+ "非圈存初始化状态!");

		// 检查指令执行结果的合法性
		List<CardApdu> apdulist = aeo.getApduList();
		if (apdulist == null || apdulist.size() != 3)
			throw new PCPSApplicationException("", "无效的APDU执行结果列表!");

		int idx = 0;

		// 检查OPEN情况
		String result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_OPEN, "寻卡失败!");

		// 检查应用存在性
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_SELECT_FILE, "选择应用目录失败!");

		// 检查圈存初始化的执行结果
		result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_LOAD_INIT, "圈存初始化失败!");
		if (PCPSUtil.isEmpty(result) || result.length() != 36)
			throw new PCPSApplicationException("", "圈存初始化失败!");

		// 解析圈存初始化结果
		result = result.substring(0, 32);
		byte[] bresult = PCPSUtil.getBytes(result);
		int off = 0;

		// EP余额
		String s_epbalance = result.substring(off * 2, off * 2 + 8);
		int epbalance = PCPSUtil.parseNetworkInteger(bresult, off, off + 4);
		old.setWalletBeforeBalance(BigInteger.valueOf(epbalance));
		off += 4;

		// EP联机交易序号
		old.setOnlineSeqNo(result.substring(off * 2, off * 2 + 4));
		off += 2;

		// 密钥版本号(DLK)
		old.setDlkVer(result.substring(off * 2, off * 2 + 2));
		off += 1;

		// 算法标识(DLK)
		old.setDlkAlg(result.substring(off * 2, off * 2 + 2));
		off += 1;

		// 伪随机数
		old.setPseudoRandom(result.substring(off * 2, off * 2 + 8));
		off += 4;

		// MAC1
		old.setMac1(result.substring(off * 2, off * 2 + 8));
		off += 4;

		// 取用4字节
		String amount = PCPSUtil
				.getHexIntegerString(old.getAmount().intValue());

		// 产生过程密钥
		// 根据应用序列号进行分散得到DLK,此处暂时写死
		String loadSK = generateLoadSK(CardApdu.DEFAULT_KEY_16, old
				.getPseudoRandom(), old.getOnlineSeqNo());

		// 验证MAC1的合法性
		boolean mac1_result = verifyLoadMac1(loadSK, s_epbalance, amount, old
				.getTerminalNo(), old.getMac1());

		if (!mac1_result) {
			old.setStatus(OnlineLoadDetail.OnlineLoad_Status_InitFail);
			onlineLoadDetailDaoImpl.updateCommit(old);
			throw new PCPSApplicationException("", "圈存初始化返回的MAC1验证失败!");
		}

		// 产生MAC2
		String mac2 = genLoadMac2(loadSK, amount, old.getTerminalNo(), old
				.getTranTime());
		old.setMac2(mac2);

		// 执行更新
		onlineLoadDetailDaoImpl.update(old);

		// 产生真正的圈存APDU指令列表
		return generateLoadApdu(old);
	}

	/**
	 * 解析圈存结果
	 * 
	 * @param am
	 * @param aeo
	 * @return
	 */
	public void parseLoadApduResult(AppManagement am, ApduExecuteObj aeo)
			throws PCPSApplicationException {
		if (aeo == null)
			throw new PCPSApplicationException("", "无效的APDU执行/结果对象!");

		String loadSeqNo = aeo.getLoadSeqNo();

		// 圈存流水
		OnlineLoadDetail old = onlineLoadDetailDaoImpl
				.getOnlineLoadDetailByLocalSeqNo(loadSeqNo);
		if (old == null)
			throw new PCPSApplicationException("", "无效的圈存流水" + loadSeqNo + "!");

		// 检查交易类型
		if (!PCPSUtil.compareObject(old.getTranType(),
				OnlineLoadDetail.OnlineLoad_TranType_Deposit))
			throw new PCPSApplicationException("", "流水" + loadSeqNo
					+ "非圈存交易流水!");

		// 检查交易状态
		if (!PCPSUtil.compareObject(old.getStatus(),
				OnlineLoadDetail.OnlineLoad_Status_WaitLoad))
			throw new PCPSApplicationException("", "流水" + loadSeqNo
					+ "非等待圈存状态!");

		// 检查指令执行结果的合法性
		List<CardApdu> apdulist = aeo.getApduList();
		if (apdulist == null || apdulist.size() != 1)
			throw new PCPSApplicationException("", "无效的APDU执行结果列表!");

		int idx = 0;

		// 检查圈存情况
		String result = AppCardApduUtil.getExpectedApdu(apdulist, idx++,
				CardApdu.APDU_LOAD, "圈存执行失败!");
		if (PCPSUtil.isEmpty(result) || result.length() != 12) {
			old.setStatus(OnlineLoadDetail.OnlineLoad_Status_LoadFail);
			throw new PCPSApplicationException("", "圈存执行失败!");
		}

		// 解析TAC
		old.setTac(result.substring(0, 8));

		// 新余额
		String newBal = PCPSUtil.getHexIntegerString(old
				.getWalletBeforeBalance().add(old.getAmount()).intValue());

		// 取用4字节
		String amount = PCPSUtil
				.getHexIntegerString(old.getAmount().intValue());

		// 校验TAC
		// 根据应用序列号进行分散得到DTK,此处暂时写死
		boolean bret = verifyTac(CardApdu.DEFAULT_KEY_16, newBal, old
				.getOnlineSeqNo(), amount, old.getTerminalNo(), old
				.getTranTime(), old.getTac());

		// 验证TAC
		if (bret) {
			old.setStatus(OnlineLoadDetail.OnlineLoad_Status_LoadSuccess);
		} else {
			old.setStatus(OnlineLoadDetail.OnlineLoad_Status_LoadFail);
			PCPSUtil.Log("LOADSEQNO[" + old.getOnlineSeqNo() + "] TAC ERRO!");
		}

		// 更新TAC
		onlineLoadDetailDaoImpl.update(old);
	}
}